﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PServiceBus.Core.Interface;
using System.Text.RegularExpressions;
using PServiceBus.Core.Runtime.Extensions;
using Phoenix.ReflectionExtensions;
using PServiceBus.Core.Runtime.Serializers;
using PServiceBus.Serializer.Xml;
using System.Collections;

namespace PServiceBus.Core.Runtime.Transformations {
    public class TextTransformation : ITransformation {
        private static readonly Regex KeyValueTextReg =
            new Regex(@"(?<Key>.*?)\s*:\s*(?<Value>.*)",
                RegexOptions.Multiline | RegexOptions.IgnoreCase |
                RegexOptions.Compiled);
        #region IDataTransformation Members

        public string Transform(Dictionary<string, object> param) {
            var sb = new StringBuilder();
            foreach (var d in param)
                if (d.Value != null)
                    sb.AppendFormat("{0}: {1}\r\n", d.Key, d.Value);
            return sb.ToString();
        }

        public string Transform(List<Dictionary<string, object>> list) {
            var sb = new StringBuilder();
            foreach (var param in list) 
                sb.AppendFormat("{0}-EOF-", Transform(param));
            return sb.ToString();
        }

        public IEnumerable<TData> Transform<TData>(string data) where TData : class {
            var tokens = Regex.Split(data, "-EOF-");
            Type generatedType = null;
            foreach (var token in tokens) {
                if (String.IsNullOrWhiteSpace(token)) continue;
                var obj = ReflectionHelper.New<TData>();
                if (generatedType == null) generatedType = obj.GetType();
                var keyValueMatches = KeyValueTextReg.Matches(token);
                foreach (Match keyValue in keyValueMatches) {
                    var prop = generatedType.GetPropertyEx(keyValue.Groups["Key"].Value);
                    if (prop == null) continue;
                    var value = keyValue.Groups["Value"].Value.TrimEnd('\r');
                    var isXmlBase64 = value.IsXmlBase64();
                    value = isXmlBase64 ? String.Format("<Data>{0}</Data>", value.XmlBase64ToXml()) : value;
                    prop.SetValueEx(obj, 
                        isXmlBase64 ? XmlSerializer.Deserialize(value, prop.PropertyType) : this.ChangeType(value, prop.PropertyType));
                }
                yield return obj;
            }
        }

        public Type GetCanonicalType(string data) {
            var xml = new StringBuilder();
            xml.Append("<List><Items type=\"Items\">");
            var tokens = Regex.Split(data, "-EOF-");
            foreach (var token in tokens) {
                if (String.IsNullOrWhiteSpace(token)) continue;
                var keyValueMatches = KeyValueTextReg.Matches(token);
                xml.Append("<Message>");
                foreach (Match keyValue in keyValueMatches) {
                    var key = keyValue.Groups["Key"].Value;
                    var value = keyValue.Groups["Value"].Value.TrimEnd('\r');
                    var isXmlBase64 = value.IsXmlBase64();
                    xml.AppendFormat("<{0}>{1}</{0}>", key, isXmlBase64 ? value.XmlBase64ToXml() : value);
                }
                xml.Append("</Message>");
                break;//Breaking because we only need to get first object to construct canonical data model
            }
            xml.Append("</Items></List>");
            return this.ToCanonicalType((JsonSerializer.XmlToJson(xml.ToString()).FromJson() as IList)[0] as Dictionary<string, object>);
        }
        #endregion
    }
}
